package com.mkb.core;

import com.mkb.ann.Autowried;
import com.mkb.ann.ComponentScan;
import com.mkb.ann.Scope;
import com.mkb.ann.Service;
import com.mkb.service.UserService;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author meikb
 * @desc
 * @date 2020-11-03 15:04
 */
public class AnnotationConfigApplicationContext {
    public ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
    public ConcurrentHashMap<String, Object> singleObjects = new ConcurrentHashMap<String, Object>();

    public AnnotationConfigApplicationContext(Class clazz){
        //1：通过给的类 获取到需要加载的bean的位置
        scan(clazz);

        //2.加载到容器
        instanceSingleBean();
    }

    private void instanceSingleBean(){
        try {
            //1:获取beanDefinitionMap 封装的类信息
            for (String beanName : beanDefinitionMap.keySet()){
                BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

                if("singleton".equals(beanDefinition.getScope())){
                    Object bean = doCreateBean(beanDefinition);

                    singleObjects.put(beanName, bean);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private Object doCreateBean(BeanDefinition beanDefinition) throws Exception{
        Class clazz = beanDefinition.getBeanClass();
        Object bean = clazz.getDeclaredConstructor().newInstance();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields){
            String beanName = field.getName();
            if(field.isAnnotationPresent(Autowried.class)){
                Object fieldBean = getBean(beanName);

                field.setAccessible(true);
                field.set(bean, fieldBean);
            }
        }
        return bean;
    }

    private void scan(Class clazz) {
        //1:获取当前类的注解 找到需要扫描的包的路径
        ComponentScan scan = (ComponentScan)clazz.getAnnotation(ComponentScan.class);
        String packagePath = scan.value();
        //获取当前包下的所有的类
        List<Class> packageClass = genBeanClasses(packagePath);

        //2:把类封装为BeanDefinition的map
        toMap(packageClass);
    }

    private void toMap(List<Class> packageClass) {
        for (Class clazz : packageClass){
            if(clazz.isAnnotationPresent(Service.class)){
                //通过当前clazz封装BeanDefinition
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setBeanClass(clazz);
                Service service = (Service) clazz.getAnnotation(Service.class);
                String beanName = service.value();
                if(clazz.isAnnotationPresent(Scope.class)){
                    Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                    String scopeValue = scope.value();
                    if("prototype".equals(scopeValue)){
                        beanDefinition.setScope("prototype");
                    }else{
                        beanDefinition.setScope("singleton");
                    }
                }
                beanDefinitionMap.put(beanName, beanDefinition);
            }

        }
    }

    /**
     *  通过com.mkb.server -> com/mkb/service 再拼接上 类的全路径  得到file文件  通过file文件 获取到文件名 就是 service名称
     *  然后通过  classLoader.loadClass(className); 获取到class 集合
     */
    private List<Class> genBeanClasses(String packagePath) {
        List<Class> beanClasses = new ArrayList<Class>();

        ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
        packagePath = packagePath.replace(".", "/");
        URL resource = classLoader.getResource(packagePath);
        File file = new File(resource.getFile());

        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                if (fileName.endsWith(".class")) {
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    className = className.replace("\\", ".");
                    try {
                        Class<?> clazz = classLoader.loadClass(className);
                        beanClasses.add(clazz);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return beanClasses;
    }

    public Object getBean(String beanName) throws Exception{
        if(!beanDefinitionMap.containsKey(beanName)){
            throw new Exception();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if("singleton".equals(beanDefinition.getScope())){
            return singleObjects.get(beanName);
        }else{
            return doCreateBean(beanDefinition);
        }
    }
}
